using System;
using System.Diagnostics;
using System.Threading;
using Rhizome.Contract;

namespace Rhizome.Model
{
    public abstract class ActiveObjectBase : IActiveObject
    {
        private readonly IActiveObjectHost _host;
        private readonly object _stopMonitor = new object();
        private bool _active;

        [DebuggerStepThrough]
        protected ActiveObjectBase(IActiveObjectHost host)
        {
            if (host == null) throw new ArgumentNullException("host");

            _host = host;
        }

        #region IActiveObject Members

        public IActiveObject Next { get; set; }
        public IActiveObject Prev { get; set; }
        public bool Sleeping { get; set; }
        public int Timeout { get; set; }
        public Exception Exception { get; set; }

        public bool Active
        {
            [DebuggerStepThrough]
            get { return _active /*Next != null*/; }
            set
            {
                if (value == _active) return;

                _active = value;

                if (value)
                    _host.Add(this);
                else
                {
                    _host.Remove(this);
                    OnStopped();
                }
            }
        }

        private void OnStopped()
        {
            lock (_stopMonitor) Monitor.PulseAll(_stopMonitor);
        }

        /// <summary>
        ///   Sleeps ActiveObject for specified interval. 
        ///   (asks "sheduler" not no run next step for specified period)
        /// </summary>
        /// <param name = "interval">The interval (msec).</param>
        public void Sleep(int interval)
        {
            if (interval > 0 && Active) _host.Sleep(this, Environment.TickCount + interval);
        }

        public void Sleep()
        {
            if (Active) _host.Sleep(this);
        }

        public void WakeUp()
        {
            if (Sleeping) _host.WakeUp(this);
        }

        public abstract void Step();

        public object StopMonitor
        {
            [DebuggerStepThrough]
            get { return _stopMonitor; }
        }

        #endregion
    }
}